
#include "TCPConnection.h"
#include "Socket.h"
#include "sock.h"
#include "eventloop/Eventloop.h"
#include "eventloop/IOchannel.h"
#include "log/Logger.h"
#include "utility/assertion.h"
#include "wrap/error.h"
#include "wrap/unistd.h"

#include <functional>


TCPConnection::TCPConnection(
    Eventloop &loop, const std::string &name, std::unique_ptr<Socket> &&socket,
    const Sockaddr &local, const Sockaddr &peer):
    _name{name},
    _closeCallback{},
    _connectionCallback{},
    _messageCallback{},
    _local{local},
    _peer{peer},
    _loop{loop},
    _socket{std::move(socket)},
    _channel{},
    _state{_State::_CONNECTING}
{
    _channel = std::make_unique<IOchannel>(_loop, _socket->fd(), name);
    _channel->set_read_callback(
        [this](Utility::timepoint t){ _read_callback(t); });
}
                  
                  
TCPConnection::~TCPConnection() 
{
    LOG_DEBUG << "TCPConnection [" << _name << "] at " << this 
              << " fd=" << _channel->fd() << " state=" << _state_to_string();
    assert(_state == _State::_DISCONNECTED);
}


void TCPConnection::set_connection_callback(const ConnectionCallback &callback) {
    assert(callback);
    _connectionCallback = callback;
}

void TCPConnection::set_close_callback(const CloseCallback &callback) {
    assert(callback);
    _closeCallback = callback;
}

void TCPConnection::set_message_callback(const MessageCallback &callback) {
    assert(callback);
    _messageCallback = callback;
}


void TCPConnection::established() {
    _loop.ASSERT_IN_THIS_THREAD();
    assert(_state == _State::_CONNECTING);
    _set_state(_State::_CONNECTED);
    _channel->tie(shared_from_this());
    _channel->enable_reading();
    _connectionCallback(shared_from_this());
}

void TCPConnection::destroy() {
    _loop.ASSERT_IN_THIS_THREAD();
    if (_state == _State::_CONNECTED) {
        _state = _State::_DISCONNECTED;
        _channel->disable();
        _connectionCallback(shared_from_this());
    }
    _channel->remove();
}

const char *TCPConnection::_state_to_string() const {
    switch (_state) {
    case _State::_DISCONNECTED:
        return "_DISCONNECTED";
        break;
    case _State::_CONNECTING:
        return "_CONNECTING";
        break;
    case _State::_CONNECTED:
        return "_CONNECTED";
        break;
    case _State::_DISCONNECTING:
        return "_DISCONNCETING";
        break;
    default:
        return "UNKNOWN STATE";
    }
}

void TCPConnection::_read_callback(Utility::timepoint t) {
    _loop.ASSERT_IN_THIS_THREAD();
    int savedErrno = 0;
    char buf[65536];
    auto n = Wrap::read_(_channel->fd(), buf, sizeof(buf));
    if (n > 0) {
        _messageCallback(shared_from_this(), buf, n);
        return;
    }
    else if (n == 0) {
        _close_callback();
        return;
    }
 
    errno = savedErrno;
    LOG_SYSERROR << "TCPConnection::read_fd<0";
    _error_callback();
    
}

void TCPConnection::_write_callback() {

}

void TCPConnection::_error_callback() {
    const auto err = Sock::socket_error(_channel->fd());
    LOG_ERROR << "TCPConnection [" << _name << "]-ERROR=" << err << ' '
              << Wrap::strerror_r_(err);
}

void TCPConnection::_close_callback() {
    _loop.ASSERT_IN_THIS_THREAD();
    LOG_VERBOSE << "fd=" << _channel->fd() << " state=" << _state_to_string();
    assert(_state == _State::_CONNECTED || _state == _State::_DISCONNECTING);
    _state = _State::_DISCONNECTED;
    _channel->disable();
    const auto guard = shared_from_this();
    _connectionCallback(guard);
    _closeCallback(guard);
}